#!/usr/bin/env python3

# Copyright (c) 2020-2021 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from typing import Iterable, Optional
from .util import is_arch_and_windows_version_match
from ._const import Const


class WineConfigurationEditor:

    def __init__(self, parent) -> None:
        '''
        This object represents the functions of winecfg.
        Note: the real winecfg is not executed.

        Parameters
        ----------
        parent: WinePrefix
            parent object
        '''

        self._parent = parent
        self._regObj = self._parent._objAltReg

    def dispose(self) -> None:
        assert not self._regObj.changed
        del self._regObj
        del self._parent

    def get_windows_version(self) -> str:
        '''
        Get Windows version of the wineprefix.

        Return
        ------
        str:
            the Windows version:
            win10 (Microsoft Windows 10)
            win81 (Microsoft Windows 8.1)
            win8 (Microsoft Windows 8)
            win7 (Microsoft Windows 7)
            win2008r2 (Microsoft Windows 2008 R1)
            win2008 (Microsoft Windows 2008)
            winxp (Microsoft Windows XP)
        '''

        ret = self._regObj.get_value("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName", value_type="REG_SZ")
        ret = _windowsVersionInfoReverseDict[ret]

        if ret == "winxp":
            curVer = self._regObj.get_value("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentVersion", value_type="REG_SZ")
            if curVer >= "5.2":
                ret = "winxp64"

        return ret

    def set_windows_version(self, version: str) -> None:
        '''
        Change Windows version of the wineprefix.

        Parameters
        ----------
        version : str
            the Windows version to be setted, see get_windows_version()
        '''

        assert is_arch_and_windows_version_match(self._parent.winearch, version)

        for value_name, data in _windowsVersionInfoDict[version].items():
            self._regObj.add_value("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", value_name, "REG_SZ", data)

    def set_app_default(self, executable: str, version: str) -> None:
        '''
        Change default Windows version per application

        Parameters
        ----------
        executable : str
            a valid executable name (e.g. wmplayer.exe)
        version : str
            the Windows version to be setted:
            win10 (Microsoft Windows 10)
            win81 (Microsoft Windows 8.1)
            win8 (Microsoft Windows 8)
            win7 (Microsoft Windows 7)
            win2008r2 (Microsoft Windows 2008 R1)
            win2008 (Microsoft Windows 2008)
            winxp (Microsoft Windows XP)
        '''

        assert version in _windowsVersionInfoDict

        self._regObj.add_value(
            key=f"HKEY_CURRENT_USER\\Software\\Wine\\AppDefaults\\{executable}",
            value_name="Version",
            value_type="REG_SZ",
            data=version
        )

    def get_virtual_desktop(self) -> None:
        '''
        Get Wine Virtual Desktop status

        Return
        ------
        tuple:
            Enable status and resolution.
        '''

        if self._regObj.has_key("HKEY_CURRENT_USER\\Software\\Wine\\Explorer"):
            for value_name, v in self._regObj.list_values(key="HKEY_CURRENT_USER\\Software\\Wine\\Explorer").items():
                if value_name == "Desktop":
                    value_type, data = v
                    assert value_type == "REG_SZ" and data == "Default"
                    for value_name2, v2 in self._regObj.list_values(key="HKEY_CURRENT_USER\\Software\\Wine\\Explorer\\Desktops").items():
                        if value_name2 == "Default":
                            value_type2, data2 = v2
                            assert value_type2 == "REG_SZ"
                            return (True, data2)
                    assert False
        return (False, None)

    def set_virtual_desktop(self, status: bool, res: str = None) -> None:
        '''
        Enable or disable the Wine Virtual Desktop.

        Parameters
        ----------
        status : bool
            the Virtual Desktop status
        res : str (optional)
            the resolution to be used (e.g. 800x600) only if status is True
        '''

        if status:
            self._regObj.add_value(
                key="HKEY_CURRENT_USER\\Software\\Wine\\Explorer",
                value_name="Desktop",
                value_type="REG_SZ",
                data="Default"
            )
            self._regObj.add_value(
                key="HKEY_CURRENT_USER\\Software\\Wine\\Explorer\\Desktops",
                value_name="Default",
                value_type="REG_SZ",
                data=res
            )
        else:
            self._regObj.delete_value(
                key="HKEY_CURRENT_USER\\Software\\Wine\\Explorer",
                value_name="Desktop"
            )

    def set_decorations(self, status: bool) -> None:
        '''
        Enable or disable the windows manager decorations.

        Parameters
        ----------
        status : bool
            the decorations status
        '''

        if status:
            status = "Y"
        else:
            status = "N"

        self._regObj.add_value(
            key="HKEY_CURRENT_USER\\Software\\Wine\\X11 Driver",
            value_name="Decorated",
            value_type="REG_SZ",
            data=status
        )

    def set_window_managed(self, status: bool) -> None:
        '''
        Enable or disable the windows manager control.

        Parameters
        ----------
        status : bool
            the control status
        '''

        if status:
            status = "Y"
        else:
            status = "N"

        self._regObj.add_value(
            key="HKEY_CURRENT_USER\\Software\\Wine\\X11 Driver",
            value_name="Managed",
            value_type="REG_SZ",
            data=status
        )

    def set_fullscreen_mouse_capture(self, status: bool) -> None:
        '''
        Enable or disable auto mouse capture in fullscreen.

        Parameters
        ----------
        status : bool
            the capture status
        '''

        if status:
            status = "Y"
        else:
            status = "N"

        self._regObj.add_value(
            key="HKEY_CURRENT_USER\\Software\\Wine\\X11 Driver",
            value_name="GrabbFullscreen",
            value_type="REG_SZ",
            data=status
        )

    def set_dpi(self, dpi: int) -> None:
        '''
        Set custom DPI value.

        Parameters
        ----------
        dpi : int
            the new density value
        '''

        self._regObj.add_value(
            key="HKEY_CURRENT_USER\\Control#Panel\\Desktop",
            value_name="LogPixels",
            value_type="REG_DWORD",
            data=dpi
        )

    def list_override_dlls(self) -> Iterable[tuple[str, str]]:
        '''
        List all DLL overrides in the wineprefix

        Return
        ------
        list:
            A list of dll overrides.
        '''

        ret = []
        if self._regObj.has_key("HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides"):
            for value_name, v in self._regObj.list_values(key="HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides").items():
                value_type, value = v
                assert value_type == "REG_SZ"
                assert value in Const.dllOverrideValueDict.values()
                ret.append((value_name, value))
        return ret

    def override_dll(self, name: str, override: Optional[str] = None, restore: bool = False) -> None:
        '''
        Overriding a DLL in the wineprefix.

        Parameters
        ----------
        name : str
            the name of the DLL
        override : str
            the type of override:
            "b" or "builtin"          : provided by Wine
            "n" or "native"           : provided by Windows
            "b,n" or "builtin,native" : builtin then native
            "n,b" or native,builtin   : native then builtin
        restore : bool
            restore the override to the initiale state (default False)
        '''

        assert f"{name}.dll" not in Const.noOverrideDllList
        if not restore:
            override = Const.dllOverrideValueDict.get(override, override)
            assert override in Const.dllOverrideValueDict.values()
        else:
            assert override is None

        if not restore:
            self._regObj.add_value(
                key="HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides",
                value_name=name,
                value_type="REG_SZ",
                data=override
            )
        else:
            self._regObj.delete_value(
                key="HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides",
                value_name=name
            )


_windowsVersionInfoDict = {
    "win11": {
        "ProductName": "Microsoft Windows 11",
        "CSDVersion": "",
        "CurrentBuild": "22000",
        "CurrentBuildNumber": "22000",
        "CurrentVersion": "6.3",
    },
    "win10": {
        "CSDVersion": "",
        "CSDVersionHex": "0",
        "CurrentBuild": "17763",
        "CurrentBuildNumber": "17763",
        "CurrentVersion": "10.0",
        "CurrentMinorVersionNumber": "0",
        "CurrentMajorVersionNumber": "a",  # 10
        "ProductType": "WinNT",
        "ProductName": "Microsoft Windows 10",
    },
    "win81": {
        "CSDVersion": "",
        "CSDVersionHex": "00000000",
        "CurrentBuild": "9600",
        "CurrentBuildNumber": "9600",
        "CurrentVersion": "6.3",
        "CurrentMinorVersionNumber": "3",
        "CurrentMajorVersionNumber": "6",
        "ProductType": "WinNT",
        "ProductName": "Microsoft Windows 8.1",
    },
    "win8": {
        "CSDVersion": "",
        "CSDVersionHex": "0",
        "CurrentBuild": "9200",
        "CurrentBuildNumber": "9200",
        "CurrentVersion": "6.2",
        "CurrentMinorVersionNumber": "2",
        "CurrentMajorVersionNumber": "6",
        "ProductType": "WinNT",
        "ProductName": "Microsoft Windows 8",
    },
    "win7": {
        "CSDVersion": "Service Pack 1",
        "CSDVersionHex": "100",
        "CurrentBuild": "7601",
        "CurrentBuildNumber": "7601",
        "CurrentVersion": "6.1",
        "CurrentMinorVersionNumber": "1",
        "CurrentMajorVersionNumber": "6",
        "ProductType": "WinNT",
        "ProductName": "Microsoft Windows 7",
    },
    "win2008r2": {
        "CSDVersion": "Service Pack 1",
        "CSDVersionHex": "100",
        "CurrentBuild": "7601",
        "CurrentBuildNumber": "7601",
        "CurrentVersion": "6.1",
        "CurrentMinorVersionNumber": "1",
        "CurrentMajorVersionNumber": "6",
        "ProductType": "ServerNT",
        "ProductName": "Microsoft Windows 2008 R2",
    },
    "win2008": {
        "CSDVersion": "Service Pack 2",
        "CSDVersionHex": "200",
        "CurrentBuild": "6002",
        "CurrentBuildNumber": "6002",
        "CurrentVersion": "6.0",
        "CurrentMinorVersionNumber": "0",
        "CurrentMajorVersionNumber": "6",
        "ProductType": "ServerNT",
        "ProductName": "Microsoft Windows 2008",
    },
    "win2003": {
        "CSDVersion": "Service Pack 2",
        "CSDVersionHex": "200",
        "CurrentBuild": "3790",
        "CurrentBuildNumber": "3790",
        "CurrentVersion": "5.2",
        "CurrentMinorVersionNumber": "2",
        "CurrentMajorVersionNumber": "5",
        "ProductType": "ServerNT",
        "ProductName": "Microsoft Windows 2003",
    },
    "winxp64": {
        "CSDVersion": "Service Pack 2",
        "CSDVersionHex": "200",
        "CurrentBuild": "3790",
        "CurrentBuildNumber": "3790",
        "CurrentVersion": "5.2",
        "CurrentMinorVersionNumber": "2",
        "CurrentMajorVersionNumber": "5",
        "ProductType": "WinNT",
        "ProductName": "Microsoft Windows XP",
    },
    "winxp": {
        "CSDVersion": "Service Pack 3",
        "CSDVersionHex": "768",
        "CurrentBuild": "2600",
        "CurrentBuildNumber": "3790",
        "CurrentVersion": "5.1",
        "CurrentMinorVersionNumber": "2",
        "CurrentMajorVersionNumber": "5",
        "ProductName": "Microsoft Windows XP",
    },
    "win98": {
        "CSDVersion": "",
        "CSDVersionHex": "00000000",
        "VersionNumber": "4.10.2222",
        "SubVersionNumber": " A ",
        "ProductName": "Microsoft Windows 98",
    },
    "win95": {
        "CSDVersion": "",
        "CSDVersionHex": "00000000",
        "VersionNumber": "4.0.950",
        "SubVersionNumber": "",
        "ProductName": "Microsoft Windows 95",
    },
}


_windowsVersionInfoReverseDict = {
    "Microsoft Windows 11": "win11",
    "Windows 10 Pro": "win10",
    "Microsoft Windows 10": "win10",
    "Microsoft Windows 8.1": "win81",
    "Microsoft Windows 8": "win8",
    "Microsoft Windows 7": "win7",
    "Microsoft Windows 2008 R2": "win2008r2",
    "Microsoft Windows 2008": "win2008",
    "Microsoft Windows XP": "winxp",
    "Microsoft Windows 98": "win98",
    "Microsoft Windows 95": "win95",
}
